home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Mac Game Programming Gurus
/
TricksOfTheMacGameProgrammingGurus.iso
/
More Source
/
Libraries
/
SpriteEngine
/
SpriteTools.c
< prev
next >
Wrap
Text File
|
1995-03-13
|
9KB
|
339 lines
// SpriteTools
// Routines to be called from the engine and from SpriteHandlers
#include "SpriteTools.h"
/*
MyNewGWorld: Creates a GWorld
LoadFaceFromCicn: Loads a face
PlotFace: Draws a face
NewSprite: Creates a sprite
DisposeSprite: Disposes a sprite
KeepOnScreen: Performs border checks for a sprite
RectSeparate: Moves two sprites apart
*/
/*Global variables*/
/* The window pointer */
WindowPtr myWindow;
/* A global pointer is the root of the entity list */
SpritePtr gSpriteList = nil;
/* GWorlds for the animation and background buffers */
GrafPtr gOffScreen, gBackScreen;
/*MyNewGWorld: Glue to NewGWorld*/
/*I declare offscreenGWorld as GrafPtr to save us a bunch of typecasts later (in CopyBits).*/
/*Most parameters to NewGWorld omitted - NewGWorld is smart enough to make the defaults useable.*/
#include <QDOffScreen.h>
void MyNewGWorld(GrafPtr *offscreenGWorld, Rect *boundsRect)
{
GDHandle saveGD;
GWorldPtr savePort;
GetGWorld(&savePort, &saveGD);
if ( noErr != NewGWorld((GWorldPtr *)offscreenGWorld, 0, boundsRect, nil, nil, pixelsLocked) )
DoError;
/*We lock the offscreen pixmap so we can CopyBits and PlotCIcon to it.*/
if ( LockPixels((*(CGrafPtr *)offscreenGWorld)->portPixMap) )
;
/*Note: We should unlock it (UnlockPixels) when not animating, to avoid memory fragmentation,*/
/*but you can bother with that later if it's a problem.*/
SetGWorld(savePort, saveGD);
}; /*MyNewGWorld*/
GrafPtr LoadFaceFromCicn(short cicnId)
{
GrafPtr offscreenGWorld;
CIconHandle theCicn;
GDHandle saveGD;
GWorldPtr savePort;
GetGWorld(&savePort, &saveGD);
theCicn = GetCIcon(cicnId);
MyNewGWorld(&offscreenGWorld, &(**theCicn).iconMask.bounds);
if ( offscreenGWorld != nil )
{
SetGWorld((GWorldPtr)offscreenGWorld, nil);
PlotCIcon(&(**theCicn).iconMask.bounds, theCicn);
/*I use the clipRgn for storing the mask region. This may seem dangerous,
but when we aren't drawing in the GWorld anyway, it won't matter.*/
if ( offscreenGWorld == nil )
offscreenGWorld->clipRgn = NewRgn ();
if ( noErr != BitMapToRegion(offscreenGWorld->clipRgn, &(**theCicn).iconMask) )/**/
offscreenGWorld->clipRgn = nil;/*or DisposeRgn?*/
DisposeCIcon(theCicn);
}
SetGWorld(savePort, saveGD);
return offscreenGWorld;
} /*LoadFaceFromCicn*/
static RgnHandle gTmpRgn = nil;
void PlotFace(GrafPtr theCicn, GrafPtr destPort, Point where)
{
GDHandle saveGD;
GWorldPtr savePort;
Rect bounds;
RGBColor saveForeColor, saveBackColor;
GetGWorld(&savePort, &saveGD);
bounds = theCicn->portRect;
OffsetRect(&bounds, where.h - bounds.left, where.v - bounds.top);
if ( gTmpRgn == nil )
gTmpRgn = NewRgn (); /*For top speed, we make this global, and create it only once!*/
CopyRgn(theCicn->clipRgn, gTmpRgn);
OffsetRgn(gTmpRgn, where.h, where.v);
SetPort(destPort); /*I assume that the device is correctly set.*/
GetForeColor(&saveForeColor);
GetBackColor(&saveBackColor);
ForeColor(blackColor);
BackColor(whiteColor);
CopyBits(&theCicn->portBits, &destPort->portBits, &theCicn->portRect, &bounds, srcCopy, gTmpRgn);
RGBForeColor(&saveForeColor);
RGBBackColor(&saveBackColor);
SetGWorld(savePort, saveGD);
} /*PlotFace*/
/*************************************/
/* Routines for sprite list handling */
/*************************************/
/* NewSprite allocates space for a new entity and puts it in the entity list */
SpritePtr NewSprite()
{
SpritePtr who;
who = (SpritePtr) NewPtr(sizeof(SpriteRecord));
if (who == nil) return nil;
if (gSpriteList != nil)
{
gSpriteList->prev = who;
}
who->next = gSpriteList;
who->prev = nil;
gSpriteList = who;
return who;
} /*NewSprite*/
/* DisposeSprite removes an entity from the list and disposes it. */
void DisposeSprite(SpritePtr who)
{
if (who == nil) return;
if (who->next != nil)
who->next->prev = who->prev;
if (who->prev != nil)
who->prev->next = who->next;
if (who == gSpriteList)
gSpriteList = who->next;
DisposePtr((Ptr)who);
} /*DisposeSprite*/
/*** End of sprite handling routines ***/
/* KeepOnScreen makes border checks to keep the sprite within the window.
on a border hit, the speed is negated in order to make the sprite bounce.
KeepOnScreen returns true if a border was hit. */
Boolean KeepOnScreen(SpritePtr theSprite)
{
Boolean returnValue = false;
if (theSprite->position.h < 0)
{
theSprite->position.h = 0;
theSprite->speed.h = abs(theSprite->speed.h);
returnValue = true;
}
if (theSprite->position.v < 0)
{
theSprite->position.v = 0;
theSprite->speed.v = abs(theSprite->speed.v);
returnValue = true;
}
if (theSprite->position.h > gOffScreen->portRect.right - theSprite->face->portRect.right)
{
theSprite->position.h = gOffScreen->portRect.right - theSprite->face->portRect.right;
theSprite->speed.h = -abs(theSprite->speed.h);
returnValue = true;
}
if (theSprite->position.v > gOffScreen->portRect.bottom - theSprite->face->portRect.bottom)
{
theSprite->position.v = gOffScreen->portRect.bottom - theSprite->face->portRect.bottom;
theSprite->speed.v = -abs(theSprite->speed.v);
returnValue = true;
}
return returnValue;
} /*KeepOnScreen*/
#ifdef _hasfixedpoint
/*Same as above, but also modifies the fixedPointPosition field*/
Boolean KeepOnScreenFixed(SpritePtr theSprite)
{
Boolean returnValue = false;
if (theSprite->position.h < 0)
{
theSprite->position.h = 0;
theSprite->fixedPointPosition.h = 0;
theSprite->speed.h = abs(theSprite->speed.h);
returnValue = true;
}
if (theSprite->position.v < 0)
{
theSprite->position.v = 0;
theSprite->fixedPointPosition.v = 0;
theSprite->speed.v = abs(theSprite->speed.v);
returnValue = true;
}
if (theSprite->position.h > gOffScreen->portRect.right - theSprite->face->portRect.right)
{
theSprite->position.h = gOffScreen->portRect.right - theSprite->face->portRect.right;
theSprite->fixedPointPosition.h = theSprite->position.h << 4;
theSprite->speed.h = -abs(theSprite->speed.h);
returnValue = true;
}
if (theSprite->position.v > gOffScreen->portRect.bottom - theSprite->face->portRect.bottom)
{
theSprite->position.v = gOffScreen->portRect.bottom - theSprite->face->portRect.bottom;
theSprite->fixedPointPosition.v = theSprite->position.v << 4;
theSprite->speed.v = -abs(theSprite->speed.v);
returnValue = true;
}
return returnValue;
} /*KeepOnScreenFixed*/
#endif
/* Moves two sprites apart, to separate them with respect to their bounding boxes. */
short RectSeparate(SpritePtr theSprite, SpritePtr anotherSprite)
{
short distance[4], shortest, shortestDistance, i;
Rect bounds1, bounds2;
bounds1 = theSprite->face->portRect;
OffsetRect(&bounds1, theSprite->position.h, theSprite->position.v);
bounds2 = anotherSprite->face->portRect;
OffsetRect(&bounds2, anotherSprite->position.h, anotherSprite->position.v);
/*Calculate the distance to separate the sprites in every direction*/
distance[0] = bounds2.top - bounds1.bottom; //up
distance[1] = bounds2.bottom - bounds1.top; //down
distance[2] = bounds2.right - bounds1.left; //right
distance[3] = bounds2.left - bounds1.right; //left
/*Find the shortest distance*/
shortest = 0;
shortestDistance = abs(distance[0]);
for (i=1; i<4; i++)
{
if (abs(distance[i]) < shortestDistance)
{
shortest = i;
shortestDistance = abs(distance[i]);
}
}
/*Move the sprite in the appropriate direction*/
switch (shortest)
{
case 0:
case 1:
theSprite->position.v += distance[shortest]; break;
case 2:
case 3:
theSprite->position.h += distance[shortest]; break;
}
return shortest;
} /*RectSeparate*/
/* Random number from 0 to range-1 */
short Rand(short range)
{
short roll;
roll = Random();
return (abs(roll) % range);
} /*Rand*/
/* Collision test using regions! */
Boolean RegionHit(SpritePtr theSprite, SpritePtr anotherSprite)
{
RgnHandle faceRegion1, faceRegion2;
Boolean result;
faceRegion1 = NewRgn();
faceRegion2 = NewRgn();
CopyRgn(theSprite->face->clipRgn, faceRegion1);
OffsetRgn(faceRegion1, theSprite->position.h, theSprite->position.v);
CopyRgn(anotherSprite->face->clipRgn, faceRegion2);
OffsetRgn(faceRegion2, anotherSprite->position.h, anotherSprite->position.v);
SectRgn(faceRegion1, faceRegion2, faceRegion1);
result = !EmptyRgn(faceRegion1);
DisposeRgn(faceRegion1);
DisposeRgn(faceRegion2);
return result;
} /*RegionHit*/
/* Split a vector v into a component p parallel to another vector d,
and a compionent n that is perpendicular to d. Useful for realistic
collision handling! */
void SplitVector(Point v, Point d, Point *p, Point *n)
{
long length2, dotProduct;
length2 = d.h * d.h + d.v * d.v; /*Squared length of "d"*/
dotProduct = v.h * d.h + v.v * d.v; /*Scalar product*/
(*p).h = d.h * dotProduct / length2;
(*p).v = d.v * dotProduct / length2;
(*n).h = v.h - (*p).h;
(*n).v = v.v - (*p).v;
} /* SplitVector */